﻿using SharpDX;
using SharpDX.Direct3D11;
using System;
using System.Runtime.InteropServices;

namespace Engine.Graphics.Cobra11
{
    internal class Cobra11LightParameter : IDisposable
    {
        #region Private Fields

        private PixelShaderBufferData bufferData;
        private ConstantBuffer<PixelShaderBufferData> constantBuffer;

        private Device device;

        /// <summary>
        /// Dither texture for C11 Undercover which is normaly assigned to slot 7 in Pixelshader.
        /// </summary>
        private ShaderResourceView ditherTexture;

        private ShaderResourceView envTexture;

        private SamplerState samplerEnvShadowMap;
        private SamplerStateDescription samplerStateDescriptionDither;
        private SamplerStateDescription samplerStateDescriptionEnvMap;
        private SamplerStateDescription samplerStateDescriptionShadowMap;
        private SamplerState samplerStateDither;
        private SamplerState samplerStateShadowMap;
        private ShaderManager shaderManager;
        private VertexShaderBufferData vbufferData;
        private ConstantBuffer<VertexShaderBufferData> vsconstantBuffer;

        #endregion Private Fields

        #region Public Constructors

        public Cobra11LightParameter(Device device, ShaderManager shaderManager)
        {
            this.device = device;
            this.shaderManager = shaderManager;
            this.samplerStateDescriptionDither = new SamplerStateDescription()
            {
                AddressU = TextureAddressMode.Clamp,
                AddressV = TextureAddressMode.Clamp,
                AddressW = TextureAddressMode.Clamp,
                BorderColor = Color4.Black,
                ComparisonFunction = Comparison.Never, // ??
                Filter = Filter.MinMagMipLinear,
                MaximumAnisotropy = 16,
                MaximumLod = float.MaxValue,
                MinimumLod = 0,
                MipLodBias = 0
            };

            this.samplerStateDither = new SamplerState(device, samplerStateDescriptionDither);

            this.samplerStateDescriptionShadowMap = new SamplerStateDescription()
            {
                AddressU = TextureAddressMode.Wrap,
                AddressV = TextureAddressMode.Wrap,
                AddressW = TextureAddressMode.Wrap,
                BorderColor = Color4.Black,
                ComparisonFunction = Comparison.Never, // ??
                Filter = Filter.MinMagMipPoint,
                MaximumAnisotropy = 16,
                MaximumLod = float.MaxValue,
                MinimumLod = 0,
                MipLodBias = 0
            };

            this.samplerStateShadowMap = new SamplerState(device, samplerStateDescriptionShadowMap);

            this.samplerStateDescriptionEnvMap = new SamplerStateDescription()
            {
                AddressU = TextureAddressMode.Wrap,
                AddressV = TextureAddressMode.Wrap,
                AddressW = TextureAddressMode.Wrap,
                BorderColor = Color4.Black,
                ComparisonFunction = Comparison.Never, // ??
                Filter = Filter.Anisotropic,
                MaximumAnisotropy = 16,
                MaximumLod = float.MaxValue,
                MinimumLod = 0,
                MipLodBias = 0
            };

            this.samplerEnvShadowMap = new SamplerState(device, samplerStateDescriptionEnvMap);

            this.ditherTexture = MaterialManager.LoadTexture(device, ".\\Shaders\\C11Undercover\\dither.dds");
            this.envTexture = MaterialManager.LoadTexture(device, ".\\Shaders\\C11Undercover\\envCube2.dds");

            this.bufferData = new PixelShaderBufferData()
            {
                ShadowsFlag = false,
                DitherFlag = false,
                LightDirection = Vector3.Normalize(new Vector3(0.1f, -0.6f, 0.1f)),
                ShadowNormalMatrix = new Matrix3x3(5.0f),
                DitherTexParams = new Vector4(1, 1, 1, 1),
                DitherClipParams = new Vector4(2, 2, 2, 2),
                ShadowNormalOffset = new Vector4(3, 3, 3, 3),
                AmbNDiff = new Vector4(1.5f, 0.002f, 1.25f, 0),
                Exposure = new Vector4(2.0f, 0.040f, 1.250f, -0.20f),
                SelCascade = new Vector4(0.007f, -0.013f, 0.5f, 1.0f),
                DecalFadeParams = new Vector4(0.16f, 0.16f, 0.8f, 0.8f),
                ScreenTex = new Vector4(2, 3, 5, 6),
                WaterAnim = new Vector4(8, 7, 5, 2),
                IllumColor = new Vector4(5, 1, 1, 1),
                ShadowMats0 = new Matrix(1, 1, 1, 9, 2, 2, 2, 9, 3, 3, 3, 9, 4, 4, 4, 9),
                ShadowMats1 = new Matrix(1, 1, 1, 9, 2, 2, 2, 9, 3, 3, 3, 9, 4, 4, 4, 9),
                ShadowMats2 = new Matrix(1, 1, 1, 9, 2, 2, 2, 9, 3, 3, 3, 9, 4, 4, 4, 9),
                ShadowMats3 = new Matrix(1, 1, 1, 9, 2, 2, 2, 9, 3, 3, 3, 9, 4, 4, 4, 9)
            };

            this.vbufferData = new VertexShaderBufferData()
            {
                AmbLevel = new Vector4(0.2013f, 0.450f, 0.450f, 10.500f),
                //AmbLevel = new Vector4(0, 0, 0, 0),
                RangeFog = new Vector4(2.718f, 0.0f, 0.0f, 0.000015f),// new Vector4(2.718f, 0.0f, 0.0f, 0.00015f),
                TerrainAmbient = new Vector4(1.00f, 1.00f, 1.0f, 0.0f), // Shadow color.
                ShadowMtx = Matrix.Identity
            };

            this.constantBuffer = new ConstantBuffer<PixelShaderBufferData>(device);
            this.constantBuffer.Buffer.DebugName = "C11 LightValues";
            this.vsconstantBuffer = new ConstantBuffer<VertexShaderBufferData>(device);
            this.vsconstantBuffer.Buffer.DebugName = "C11 Vs LightValues";
            this.DirectUpdate = true;
        }

        #endregion Public Constructors

        #region Public Properties

        public Vector4 AmbNDiff
        {
            get { return bufferData.AmbNDiff; }
            set
            {
                bufferData.AmbNDiff = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        public bool DirectUpdate { get; set; }

        public Vector4 DitherClipParams
        {
            get { return bufferData.DitherClipParams; }
            set
            {
                bufferData.DitherClipParams = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        public bool DitherFlag
        {
            get { return bufferData.DitherFlag; }
            set
            {
                bufferData.DitherFlag = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        public Vector4 Exposure
        {
            get { return bufferData.Exposure; }
            set
            {
                bufferData.Exposure = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        public Vector3 LightDirection
        {
            get { return bufferData.LightDirection; }
            set
            {
                bufferData.LightDirection = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        public Vector4 SelCascade
        {
            get { return bufferData.SelCascade; }
            set
            {
                bufferData.SelCascade = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        public bool ShadowFlag
        {
            get { return bufferData.ShadowsFlag; }
            set
            {
                bufferData.ShadowsFlag = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        public Matrix ShadowMats
        {
            get { return bufferData.ShadowMats1; }
            set
            {
                bufferData.ShadowMats1 = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        public Matrix3x3 ShadowNormalMatrix
        {
            get { return bufferData.ShadowNormalMatrix; }
            set
            {
                bufferData.ShadowNormalMatrix = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        public Vector4 ShadowNormalOffset
        {
            get { return bufferData.ShadowNormalOffset; }
            set
            {
                bufferData.ShadowNormalOffset = value;

                if (this.DirectUpdate)
                    UpdateValues();
            }
        }

        #endregion Public Properties

        #region Public Methods

        public void Dispose()
        {
            this.ditherTexture.Dispose();
            this.envTexture.Dispose();
            this.constantBuffer.Dispose();
            this.vsconstantBuffer.Dispose();
            this.samplerStateShadowMap.Dispose();
            this.samplerStateDither.Dispose();
        }

        public void UpdateValues()
        {
            lock (this.device.ImmediateContext)
            {
                // Set dither texture for c11 pixelshader.
                this.device.ImmediateContext.PixelShader.SetSampler(15, this.samplerStateDither);
                this.device.ImmediateContext.PixelShader.SetShaderResource(6, this.ditherTexture);
                // Set ShadowMap texture for c11 pixelshader.
                Graphics.Shaders.ShaderBase.SetSamplerState(7, this.samplerStateShadowMap, this.device);
                Graphics.Shaders.ShaderBase.SetSamplerState(9, this.samplerStateShadowMap, this.device);
                Graphics.Shaders.ShaderBase.SetTexture(7, this.ditherTexture, this.device);
                Graphics.Shaders.ShaderBase.SetTexture(9, this.envTexture, this.device);
                //this.device.ImmediateContext.PixelShader.SetSampler(7, this.samplerStateShadowMap);
                //this.device.ImmediateContext.PixelShader.SetShaderResource(7, this.ditherTexture);

                this.constantBuffer.UpdateValue(this.bufferData);
                this.device.ImmediateContext.PixelShader.SetConstantBuffer(1, this.constantBuffer.Buffer); // PS
                this.vsconstantBuffer.UpdateValue(this.vbufferData);
                this.device.ImmediateContext.VertexShader.SetConstantBuffer(2, this.vsconstantBuffer.Buffer); // VS
            }
        }

        #endregion Public Methods

        #region Private Structs

        [StructLayout(LayoutKind.Explicit, Size = 512)]
        private struct PixelShaderBufferData
        {
            [FieldOffset(0)]
            public bool ShadowsFlag;

            [FieldOffset(4)]
            public bool DitherFlag;

            [FieldOffset(16)]
            public Vector3 LightDirection;

            [FieldOffset(32)]
            public Matrix3x3 ShadowNormalMatrix;

            [FieldOffset(80)]
            public Vector4 DecalFadeParams;

            [FieldOffset(96)]
            public Vector4 DitherTexParams;

            [FieldOffset(112)]
            public Vector4 DitherClipParams;

            [FieldOffset(128)]
            public Vector4 ShadowNormalOffset;

            [FieldOffset(144)]
            public Vector4 AmbNDiff;

            [FieldOffset(160)]
            public Vector4 Exposure;

            [FieldOffset(176)]
            public Vector4 SelCascade;

            [FieldOffset(192)]
            public Vector4 IllumColor;

            [FieldOffset(208)]
            public Vector4 WaterAnim;

            [FieldOffset(224)]
            public Vector4 ScreenTex;

            [FieldOffset(240)]
            public Matrix ShadowMats0;

            [FieldOffset(304)]
            public Matrix ShadowMats1;

            [FieldOffset(368)]
            public Matrix ShadowMats2;

            [FieldOffset(432)]
            public Matrix ShadowMats3;
        }

        [StructLayout(LayoutKind.Explicit, Size = 112)]
        private struct VertexShaderBufferData
        {
            [FieldOffset(0)]
            public Matrix ShadowMtx;

            [FieldOffset(64)]
            public Vector4 AmbLevel;

            [FieldOffset(80)]
            public Vector4 RangeFog;

            [FieldOffset(96)]
            public Vector4 TerrainAmbient;
        }

        #endregion Private Structs
    }
}